﻿using System;
using System.Collections.Generic;
using System.Linq;
using XDCCManager.Model.Actions;
using XDCCManager.Model.Packs;

namespace XDCCManager.Network.IRC
{
	public sealed class PackUpdatesManager
	{
		private System.Collections.ObjectModel.ObservableCollection<ActionGroup> actionQueue;
		private XDCCManager.Model.SortedObservableCollection<Pack> packList;
		private Queue<Tuple<ActionGroup, IPackAction>> queuedActions;
		private enum LoadingState : byte
		{
			Idle,
			CommandSent,
			Started,
			Reading,
			Ended
		}
		private LoadingState state;
		private bool update;

		public PackUpdatesManager(System.Collections.ObjectModel.ObservableCollection<ActionGroup> actionQueue, XDCCManager.Model.SortedObservableCollection<Pack> packList)
		{
			this.actionQueue = actionQueue;
			this.packList = packList;
			state = LoadingState.Idle;
			actionQueue.CollectionChanged += actionQueue_CollectionChanged;
			queuedActions = new Queue<Tuple<ActionGroup, IPackAction>>();
			ChatConnection.Instance.ConnectionReady += new Action(() => { ClearPackList(); LoadPackList(); });
			ChatConnection.Instance.ConnectionClosed += new Action(() => { ClearUpdates(); ClearPackList(); });
			ChatConnection.Instance.MessageReceived += Chat_MessageReceived;
			state = LoadingState.Idle;
			UpdatePackListHandler = () =>
			{
				if (state != LoadingState.Idle)
					return;
				update = true;
				ChatConnection.Instance.SendMessage(ChatMessages.CommandOfferedFiles + '\n');
				state = LoadingState.CommandSent;
			};
			if (ChatConnection.Instance.Connected)
				LoadPackList();
		}

		void actionQueue_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			switch (e.Action)
			{
				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
					foreach (var group in e.NewItems)
					{
						(group as ActionGroup).PropertyChanged += ActionGroup_PropertyChanged;
					}
					break;
				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
					foreach (var group in e.OldItems)
					{
						(group as ActionGroup).PropertyChanged -= ActionGroup_PropertyChanged;
					}
					break;
			}
		}

		void ActionGroup_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			if (e.PropertyName == "FirstAction")
			{
				if ((sender as ActionGroup).FirstAction is IPackAction)
				{
					lock (queuedActions)
					{
						queuedActions.Enqueue(new Tuple<ActionGroup, IPackAction>(sender as ActionGroup, (sender as ActionGroup).FirstAction as IPackAction));
					}
				}
			}
		}

		internal void LoadPackList()
		{
			if (state != LoadingState.Idle)
				return;
			update = false;
			ChatConnection.Instance.SendMessage(ChatMessages.CommandOfferedFiles + '\n');
			state = LoadingState.CommandSent;
		}

		void Chat_MessageReceived(object sender, LineEventArgs e)
		{
			string line = IRCEngine.MessageConverter.RemoveFormatChars(e.Line);
			switch (state)
			{
				case LoadingState.CommandSent:
					if (ChatMessages.ResponseOfferedFilesStart.IsMatch(line))
						state = LoadingState.Started;
					break;
				case LoadingState.Started:
					if (ChatMessages.ResponseOfferedFilesPack.IsMatch(line))
					{
						state = LoadingState.Reading;
						goto case LoadingState.Reading;
					}
					break;
				case LoadingState.Reading:
					System.Text.RegularExpressions.Match match = null;
					if ((match = ChatMessages.ResponseOfferedFilesPack.Match(line)).Success)
					{
						bool add = true;
						if (update)
						{
							if (packList.Count > int.Parse(match.Groups["number"].Value))
							{
								add = false;
								System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => {
									Pack pack = packList[int.Parse(match.Groups["number"].Value)];
									pack.Description = match.Groups["name"].Value;
									pack.Gets = int.Parse(match.Groups["gets"].Value);
									pack.Size = parseSize(match.Groups["size"].Value);
								}));
							}
						}
						if (add)
						{
							Pack pack = new Pack() { Number = int.Parse(match.Groups["number"].Value), Description = match.Groups["name"].Value, Gets = int.Parse(match.Groups["gets"].Value), Size = parseSize(match.Groups["size"].Value) };
							System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => packList.Add(pack)));
						}
					}
					else if (ChatMessages.ResponseOfferedFilesEnd.IsMatch(line))
						state = LoadingState.Ended;
					break;
				case LoadingState.Ended:
					if (!ChatMessages.ResponseOfferedFilesEnd.IsMatch(line))
						state = LoadingState.Idle;
					break;
			}
		}

		private int parseSize(string sizeValue)
		{
			if (sizeValue.Length == 0)
				return 0;
			int mul = 1;
			switch (sizeValue[sizeValue.Length - 1])
			{
				case 'G':
					mul <<= 10;
					goto case 'M';
				case 'M':
					mul <<= 10;
					goto case 'K';
				case 'K':
					mul <<= 10;
					break;
			}
			if (mul != 1)
				sizeValue = sizeValue.Substring(0, sizeValue.Length - 1);
			double size = double.Parse(sizeValue, System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture);
			size *= mul;
			return (int)Math.Round(size);
		}

		internal void ClearUpdates()
		{
			System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
			{
				foreach (var group in actionQueue.ToArray())
				{
					foreach (var action in group.Actions.ToArray().OfType<IPackAction>())
					{
						group.Actions.Remove(action);
					}
				}
			}));
		}

		internal void ClearPackList()
		{
			System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
			{
				packList.Clear();
			}));
		}

		public static void UpdatePackList()
		{
			UpdatePackListHandler();
		}

		private static Action UpdatePackListHandler;
	}
}
